﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevelopStudio.Basic;
using DevelopStudio.Control.Basic;
using CommonManage = DevelopStudio.Common.Manage;
using CommonBasic = DevelopStudio.Common.Basic;
using System.IO;
using System.Diagnostics;
using SharpSvn;
using System.Threading;

namespace DevelopStudio.Control.Commit
{
    public partial class CommitGeneralPageControl : ChangelistPageControl
    {
        bool outOfDateError = false;
        bool otherError = false;
        string itemPath = null;

        private const string ActionChangelistTitle = "ActionChangelistTitle";
        private const string ActionContainTitle = "ActionContainTitle";

        List<SvnStatusEventArgs> directoriesSvnStatus;
        List<SvnStatusEventArgs> filesSvnStatus;

        private string errorMessage;

        public string ErrorMessage
        {
            get
            {
                return errorMessage;
            }

            set
            {
                errorMessage = value;
                this.UpdateActionStatus(ActionChangelistTitle, SmsSummaryAction.ActionStatus.CompleteWithErrors);
                this.UpdateActionStatus(ActionContainTitle, SmsSummaryAction.ActionStatus.CompleteWithErrors);
                this.AddActionErrorMessage(ActionContainTitle, errorMessage);
            }
        }

        public CommitGeneralPageControl()
        {
            InitializeComponent();
        }

        public CommitGeneralPageControl(ChangelistInfo changelistInfo) : base(changelistInfo)
        {
            InitializeComponent();
        }

        public override void InitializePageControl()
        {
            base.InitializePageControl();

            this.textBoxName.Text = this.ChangelistInfo.Name;
            this.textBoxPlatform.Text = this.ChangelistInfo.WorkingCopyPath;

            filesSvnStatus = Utility.GetChangeListCommitFiles(this.ChangelistInfo, out directoriesSvnStatus);

            int count = 0;
            foreach (SvnStatusEventArgs var in filesSvnStatus)
            {
                ListViewItem item = new ListViewItem(new string[] {
                var.FullPath.Replace(this.ChangelistInfo.WorkingCopyPath, ""),
                Path.GetExtension(var.FullPath),
                CommonManage.Utility.StatusDisplayName((CommonBasic.SvnStatus)var.LocalNodeStatus),
                var.Revision.ToString()
                });
                item.Tag = var;

                if ((CommonBasic.SvnStatus)var.LocalNodeStatus == CommonBasic.SvnStatus.Deleted)
                {
                    item.BackColor = System.Drawing.Color.Red;
                }
                else if (count % 2 == 0)
                {
                    item.BackColor = System.Drawing.SystemColors.ScrollBar;
                }

                this.listViewChangelist.Items.Add(item);

                ++count;
            }
        }

        public override bool ValidatePage()
        {
            filesSvnStatus = Utility.GetChangeListCommitFiles(this.ChangelistInfo, out directoriesSvnStatus);

            foreach (SvnStatusEventArgs var in filesSvnStatus)
            {
                if ((CommonBasic.SvnStatus)var.LocalNodeStatus == CommonBasic.SvnStatus.Conflicted ||
                    (CommonBasic.SvnStatus)var.LocalNodeStatus == CommonBasic.SvnStatus.NotVersioned ||
                    (CommonBasic.SvnStatus)var.LocalNodeStatus == CommonBasic.SvnStatus.Normal &&
                    var.Revision != -1)
                {
                    MessageBox.Show(string.Format(Properties.Resources.ErrorSvnFileStatus, var.FullPath, var.LocalNodeStatus));
                    return false;
                }
            }

            return base.ValidatePage();
        }

        public override void OnAddSummary(SummaryRequestHandler handler)
        {
            base.OnAddSummary(handler);

            this.AddAction(ActionChangelistTitle, Properties.Resources.ActionContentChangelistTitle);
            this.AddActionDetailMessage(ActionChangelistTitle, this.ChangelistInfo.Name);
            this.AddAction(string.Empty, string.Empty);

            this.AddAction(ActionContainTitle, Properties.Resources.ActionContentContainFileTitle);

            foreach (ListViewItem var in this.listViewChangelist.Items)
            {
                this.AddActionDetailMessage(ActionContainTitle, var.SubItems[0].Text + " " + var.SubItems[2].Text + " " + var.SubItems[3].Text);
            }
            this.AddAction(string.Empty, string.Empty);
        }

        private void listViewChangelist_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                foreach (ListViewItem var in this.listViewChangelist.SelectedItems)
                {
                    this.OpenDiffFiles(var);
                    break;
                }
            }
        }

        private void listViewChangelist_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            foreach (ListViewItem var in this.listViewChangelist.SelectedItems)
            {
                this.OpenDiffFiles(var);
                break;
            }
        }

        private void OpenDiffFiles(ListViewItem var)
        {
            SvnStatusEventArgs bugStatus = var.Tag as SvnStatusEventArgs;

            if (bugStatus == null)
            {
                return;
            }

            string param = @"/command:diff /path:" + bugStatus.FullPath;
            
            Process.Start("TortoiseProc.exe", param);
        }

        public override void ApplyChanges()
        {
            base.ApplyChanges();
        }

        public override bool TestPostChanges()
        {
            Thread.Sleep(2000);
            //this.UpdateActionStatus(ActionChangelistTitle, SmsSummaryAction.ActionStatus.CompleteWithErrors);
            //this.UpdateActionStatus(ActionContainTitle, SmsSummaryAction.ActionStatus.CompleteWithErrors);
            //this.AddActionErrorMessage(ActionContainTitle, "提交失败，请检查网络配置！");
            //return false;

            this.UpdateActionStatus(ActionChangelistTitle, SmsSummaryAction.ActionStatus.CompleteWithSuccess);
            this.UpdateActionStatus(ActionContainTitle, SmsSummaryAction.ActionStatus.CompleteWithSuccess);

            return base.TestPostChanges();
        }

        public override bool PostApply()
        {
            this.outOfDateError = false;
            this.otherError = false;

            List<string> commitFilePath = new List<string>();

            foreach (SvnStatusEventArgs var in this.directoriesSvnStatus)
            {
                if (var.LocalNodeStatus == SvnStatus.NotVersioned)
                {
                    if (Utility.SvnClient.Add(var.FullPath) == false)
                    {
                        this.ErrorMessage = string.Format(Properties.Resources.ErrorAddDirectory, var.FullPath);
                        return false;
                    }
                }
                commitFilePath.Add(var.FullPath);
            }

            foreach (SvnStatusEventArgs var in this.filesSvnStatus.Where(o => o.LocalNodeStatus == SvnStatus.Missing))
            {
                if (Utility.SvnClient.Delete(var.FullPath) == false)
                {
                    this.ErrorMessage = string.Format(Properties.Resources.ErrorAddDirectory, var.FullPath);
                    return false;
                }
            }

            commitFilePath.AddRange(this.filesSvnStatus.Select(o =>o.FullPath).ToArray());

            SvnCommitArgs commitArgs = new SvnCommitArgs()
            {
                LogMessage = this.ChangelistInfo.CommitComment,
                Depth = SvnDepth.Empty,
                KeepLocks = false,
                KeepChangeLists = false,
                RunTortoiseHooks = false
            };

            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_WC_NOT_UP_TO_DATE);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_CLIENT_FORBIDDEN_BY_SERVER);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_CLIENT_NO_LOCK_TOKEN);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_IO_INCONSISTENT_EOL);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_FS_TXN_OUT_OF_DATE);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_RA_OUT_OF_DATE);
            commitArgs.AddExpectedError(SvnErrorCode.SVN_ERR_WC_FOUND_CONFLICT);
            commitArgs.Notify += SvnClient_Notify;

            SvnCommitResult commitResult;
            bool ok = false;

            try
            {
                ok = Utility.SvnClient.Commit(commitFilePath, commitArgs, out commitResult);
            }
            catch(Exception ex)
            {
                this.ErrorMessage = Utility.TraceException(ex);
                return false;
            }

            if (!ok && commitArgs.LastException != null)
            {
                if (!outOfDateError && !otherError)
                    outOfDateError = true; // Remaining errors are handled as exception

                StringBuilder outOfDateMessage = new StringBuilder();
                Exception ex = commitArgs.LastException;

                while (ex != null)
                {
                    outOfDateMessage.AppendLine(ex.Message);
                    ex = ex.InnerException;
                }

                if (!string.IsNullOrEmpty(itemPath))
                {
                    outOfDateMessage.AppendLine();
                    outOfDateMessage.AppendFormat(Properties.Resources.WhileCommittingX, itemPath);
                }

                if (commitResult != null &&
                    string.IsNullOrEmpty(commitResult.PostCommitError) == false)
                {
                    outOfDateMessage.AppendLine(Properties.Resources.CommitErrorMessage2);
                    outOfDateMessage.AppendLine(commitResult.PostCommitError);
                }

                this.ErrorMessage = outOfDateMessage.ToString();
                return false;
            }
            else
            {
                string oldChangelistName = this.ChangelistInfo.Name;
                this.ChangelistInfo.Name = commitResult.Revision.ToString();
                this.ChangelistInfo.Status = (int)AppEnum.ChangelistStatus.CommitedButWaiting;

                this.ClearActionDetails(ActionContainTitle);

                foreach (SvnStatusEventArgs var in this.filesSvnStatus)
                {
                    this.AddActionDetailMessage(ActionContainTitle, var.Path + " " + var.LocalNodeStatus + " " + commitResult.Revision);
                }

                this.UpdateActionStatus(ActionChangelistTitle, SmsSummaryAction.ActionStatus.CompleteWithSuccess);
                this.UpdateActionStatus(ActionContainTitle, SmsSummaryAction.ActionStatus.CompleteWithSuccess);

                try
                {
                    filesSvnStatus = Utility.GetChangeListCommitFiles(this.ChangelistInfo, out directoriesSvnStatus);

                    SvnRemoveFromChangeListArgs svnRemoveFromChangeListArgs = new SvnRemoveFromChangeListArgs() {};
                    svnRemoveFromChangeListArgs.ChangeLists.Add(oldChangelistName);

                    foreach (SvnStatusEventArgs var in this.filesSvnStatus)
                    {
                        if ((CommonBasic.SvnStatus)var.LocalNodeStatus == CommonBasic.SvnStatus.Normal &&
                            var.Revision > 0)
                        {
                            Utility.SvnClient.RemoveFromChangeList(var.FullPath, svnRemoveFromChangeListArgs);
                        }
                    }
                }
                catch(Exception)
                {
                }
            }

            return base.PostApply();
        }

        void SvnClient_Notify(object sender, SvnNotifyEventArgs e)
        {
            SvnCommitArgs ca = sender as SvnCommitArgs;

            if (ca == null)
            {
                return;
            }

            switch (e.Action)
            {
                case SvnNotifyAction.FailedOutOfDate:
                    if (e.Error != null)
                        ca.AddExpectedError(e.Error.SvnErrorCode); // Don't throw an exception for this error
                    this.outOfDateError = true;
                    itemPath = itemPath ?? e.FullPath;
                    break;
                case SvnNotifyAction.FailedConflict:
                case SvnNotifyAction.FailedMissing:
                case SvnNotifyAction.FailedNoParent:
                case SvnNotifyAction.FailedLocked:
                case SvnNotifyAction.FailedForbiddenByServer:
                    if (e.Error != null)
                        ca.AddExpectedError(e.Error.SvnErrorCode); // Don't throw an exception for this error
                    this.otherError = true;
                    itemPath = itemPath ?? e.FullPath;
                    break;
            }
        }
    }
}
